#include "map.h"


Map::Map(int rows, int columns, int width, int height)
{
    this->setRows(rows);
    this->setColumns(columns);
    this->setWidth(width);
    this->setHeight(height);

    this->setDoor(NULL);
    this->initMatrix();
}

Map::~Map()
{
    delete m_door;
}

int Map::getRows() {return this->m_rows;}
int Map::getColumns() {return this->m_columns;}
int Map::getWidth() {return this->m_width;}
int Map::getHeight() {return this->m_height;}
Door *Map::getDoor() {return this->m_door;}

MapItem *Map::getMapItem(int i, int j)
{
    if((j < 0) || (j >= getColumns()) ||
       (i < 0) || (i >= getRows())) return NULL;

    return m_matrix[i][j];
}

QRect Map::getGeometryAt(int i, int j)
{
    int width = m_width / m_columns;
    int height = m_height / m_rows;

    int left = j * width;
    int top = i * height;

    return QRect(left, top, width, height);
}

void Map::getMapItemContainingCoord(int x, int y, int &row, int &column)
{
    int i = ((y+1) * getRows()) / getHeight();
    int j = ((x+1) * getColumns()) / getWidth();

    row = i;
    column = j;
}


QList<MapItem*> Map::getMapItemsContaining(Unit *unit)
{
    QList<MapItem*> list;
    int left, top, right, bottom;
    MapItem *item;
    int leftTopRow, rightBottomRow, leftTopColumn, rightBottomColumn;

    unit->getGeometry(left, top, right, bottom);

    getMapItemContainingCoord(left, top, leftTopRow, leftTopColumn);
    getMapItemContainingCoord(right, bottom, rightBottomRow, rightBottomColumn);


    for(int i = leftTopRow; i <= rightBottomRow; i++)
    {
        for(int j = leftTopColumn; j <= rightBottomColumn; j++)
        {
            item = getMapItem(i, j);
            if((item != NULL) && !list.contains(item)) list.append(item);
        }
    }

    return list;
}


void Map::setDoor(Door *door) {this->m_door = door;}
void Map::setMapItem(int i, int j, MapItem *item)
{
    this->m_matrix[i][j] = item;
    item->setRow(i);
    item->setColumn(j);
    if(item == NULL) return;

    QRect r = getGeometryAt(i,j);

    for(int i = 0; i < item->getNbUnits(); i++)
        item->getUnitAt(i)->setgeometry(r);
}


void Map::setRows(int rows) {this->m_rows = rows;}
void Map::setColumns(int columns) {this->m_columns = columns;}
void Map::setWidth(int width) {this->m_width = width;}
void Map::setHeight(int height) {this->m_height = height;}




void Map::initMatrix()
{
    m_matrix.resize(m_rows);

    for(int i = 0; i < m_rows; i++)
    {
        m_matrix[i].resize(m_columns);
        for(int j = 0; j < m_columns; j++)
        {
            m_matrix[i][j] = NULL;
        }
    }
}


void Map::removeMapItem(int i, int j)
{
    delete m_matrix[i][j];
    m_matrix[i][j] = NULL;
}

int Map::countFruits()
{
    int nbFruits = 0;
    for(int i = 0 ; i < m_rows ; i++)
    {
        for(int j = 0; j < m_columns; j++)
        {
            if(m_matrix[i][j])
            {
                for (int k = 0 ; k < m_matrix[i][j]->getNbUnits() ; k++)
                    if (m_matrix[i][j]->getUnitAt(k)->getType() == Unit::Fruit)
                        nbFruits++;
            }
        }
    }
    return nbFruits;
}


void Map::draw(QPainter *painter)
{
    ///on dessine l'interieur des cases
    for(int i = 0; i < m_rows; i++)
        for(int j = 0; j < m_columns; j++)
            if(m_matrix[i][j] != NULL) getMapItem(i, j)->draw(painter);


    //la couleur des traits
    painter->setPen(Qt::black);


    //on dessine le contour
    painter->drawRect(QRect(0, 0, m_width-1, m_height-1));

    //on dessine les lignes horizontales
    //for(int i = 0; i < m_height; i += m_height/m_rows)
    //    painter->drawLine(0, i, m_width, i);

    //on dessine les lignes verticales
    //for(int i = 0; i < m_width; i += m_width/m_columns)
    //    painter->drawLine(i, 0, i, m_height);
}


bool Map::digBlock(int i, int j)
{
    MapItem *item = m_matrix.at(i).at(j);
    int pos = item->findUnit(Unit::Block);
    Block *block = (Block*)item->getUnitAt(pos);

    block->dig();
    if(block->isDead())
    {
        item->removeUnitAt(pos);
        if(item->isEmpty()) removeMapItem(i, j);
        return true;
    }

    return false;
}


bool Map::digBlock(MapItem *item)
{
    return digBlock(item->getRow(), item->getColumn());
}

bool Map::eatFruit(int i, int j)
{
    MapItem *item = m_matrix.at(i).at(j);
    int pos = item->findUnit(Unit::Fruit);
    item->removeUnitAt(pos);
    if(item->isEmpty()) removeMapItem(i, j);
        return true;
    return false;
}

bool Map::eatFruit(MapItem *item)
{
    return eatFruit(item->getRow(), item->getColumn());
}


void Map::serializeDebug(QDebug &dbg)
{
    dbg << "Map("
        << "Rows:" << m_rows
        << "Columns:" << m_columns
        << "Width:" << m_width
        << "Height:" << m_height;

    for(int i = 0; i < m_rows; i++)
    {
        for(int j = 0; j < m_columns; j++)
        {
            dbg << "case:" << i << j;
            if(m_matrix[i][j] == NULL)
                dbg << "vide" << (qint32)0;
            else
            {
                dbg << "nb item:" << (qint32)m_matrix[i][j]->getNbUnits();
                dbg << m_matrix[i][j];
            }
        }
    }

    dbg << ")";
}

void Map::serializeOut(QDataStream &out)
{
    out << (qint32)m_rows;
    out << (qint32)m_columns;
    out << (qint32)m_width;
    out << (qint32)m_height;

    for(int i = 0; i < m_rows; i++)
    {
        for(int j = 0; j < m_columns; j++)
        {
            if(m_matrix[i][j] == NULL)
                out << (qint32)0;
            else
            {
                out << (qint32)m_matrix[i][j]->getNbUnits();
                out << m_matrix[i][j];
            }
        }
    }
}

void Map::serializeIn(QDataStream &in)
{
    in >> m_rows;
    in >> m_columns;
    in >> m_width;
    in >> m_height;

    int nbUnits;
    MapItem *item;

    for(int i = 0; i < m_rows; i++)
    {
        for(int j = 0; j < m_columns; j++)
        {
            in >> nbUnits;
            if(nbUnits > 0)
            {
                item = new MapItem();
                in >> item;
                setMapItem(i, j, item);
            }
        }
    }
}

QDataStream & operator << (QDataStream &out, Map* map)
{
    map->serializeOut(out);
    return out;
}

QDataStream & operator >> (QDataStream &in,  Map* map)
{
    map->serializeIn(in);
    return in;
}

QDebug operator<<(QDebug dbg, Map *map)
{
    map->serializeDebug(dbg);
    return dbg;
}

